/*
 * @Descripttion: vite 配置文件
 * @version:
 * @Author: lhl
 * @Date: 2024-09-01 10:27:00
 * @LastEditors: lhl
 * @LastEditTime: 2024-10-23 10:29:23
 */
import { defineConfig, loadEnv } from 'vite';
import type { UserConfig, ConfigEnv } from 'vite';
import react from '@vitejs/plugin-react';
import path, { resolve } from 'path';
import autoprefixer from 'autoprefixer';
import { visualizer } from 'rollup-plugin-visualizer';
import legacy from '@vitejs/plugin-legacy';
import viteCompression from 'vite-plugin-compression';
import imageminPlugin from 'vite-plugin-imagemin';
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import terser from '@rollup/plugin-terser';
import ViteRestart from 'vite-plugin-restart';
import { fileURLToPath } from 'url';
// mock
import { viteMockServe } from 'vite-plugin-mock';

interface PreRenderedAsset {
  name: string | undefined;
  originalFileName: string | null;
  source: string | Uint8Array;
  type: 'asset';
}

// 用于处理预渲染的静态资源
const isImageFile = (assetInfo: { name?: string }) => {
  const imageExtensions = /\.(png|jpe?g|gif|svg|webp)$/i;
  return assetInfo.name?.match(imageExtensions) !== null;
};

// https://vitejs.dev/config/
// command 可以区分环境
export default defineConfig(({ command, mode }: ConfigEnv): UserConfig => {
  // 根据当前工作目录中的 `mode` 加载 .env 文件
  // 设置第三个参数为 '' 来加载所有环境变量，而不管是否有 `VITE_` 前缀。
  const env = loadEnv(mode, process.cwd(), '');
  const isProd = mode === 'production';
  return {
    publicDir: fileURLToPath(new URL('./public', import.meta.url)), // 无需处理的静态资源位置
    assetsInclude: fileURLToPath(new URL('./src/assets', import.meta.url)), // 需要处理的静态资源位置
    // 开发时候是./ 生产时候 / 开发或生产环境服务的公共基础路径 /适用于应用程序部署在服务器根目录的情况 ./适用于应用程序部署在子目录或需要相对路径的情况
    // base: '/',
    // 对于使用 TypeScript 的开发者来说，请确保在 env.d.ts 或 vite-env.d.ts 文件中添加类型声明，以获得类型检查以及代码提示 可以在ts/js文件中获取
    // console.log('Environment:', __APP_ENV__);
    // console.log('API URL:', __APP_BASE_URL__);
    define: {
      __APP_ENV__: JSON.stringify(env),
      __APP_BASE_URL__: JSON.stringify(env.VITE_APP_BASE_URL)
    },
    server: {
      host: '0.0.0.0', // 允许所有 IP 访问
      port: 3000,
      open: true, // 自动打开浏览器
      cors: true // 允许跨域
      // 跨域相关
      // proxy: {
      //   '/api': {
      //     target: '', // 目标地址
      //     changeOrigin: true, // 是否跨域
      //     rewrite: (path) => path.replace(/^\/api/, '') // 重写地址，路径去掉 /api
      //   }
      // }
    },
    css: {
      // 这里留空，Vite 会自动加载 postcss.config.mjs 要么这里配置也可以 要么就 postcss.config.mjs取一种即可
      postcss: {
        plugins: [
          // 配置 autoprefixer 自动加前缀
          autoprefixer({
            overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8', '> 1%'],
            grid: true
          })
        ]
      },
      preprocessorOptions: {
        scss: {
          charset: false,
          javascriptEnabled: true,
          // 这里可以配置SASS的全局变量等
          additionalData: `@use "./src/styles/variables.scss" as *;`
        }
      }
    },
    resolve: {
      // 配置路径别名 对应ts配置 paths
      alias: {
        '@': resolve(__dirname, './src'),
        '@utils': resolve(__dirname, './src/utils'),
        '@components': resolve(__dirname, './src/components')
      }
    },
    plugins: [
      react(),
      viteMockServe({
        mockPath: 'mock', // Mock 文件所在的目录
        enable: command === 'serve' // 只有开发环境才开启mock
        // watchFiles: true // 监听 Mock 文件变化时自动重启 默认true可不配置
      }),
      ViteRestart({
        // 一般就是这两个文件
        restart: ['vite.config.ts', '.env.development']
      }),
      visualizer({
        filename: 'stats.html', // 输出文件名
        open: true // 是否自动打开浏览器查看
      }),
      // 兼容性处理 一般配置 targets: ['defaults', 'not IE 11']
      legacy({
        targets: ['ie >= 11'],
        additionalLegacyPolyfills: ['regenerator-runtime/runtime'] // 只加载必要的 polyfill
        // 下面的配置可选 多了打包变慢
        // targets: [
        //   'defaults', // 使用默认设置，可能覆盖大部分需求
        //   'not IE 11', // 如果不需要支持IE 11，可以将其排除
        //   '> 1%, last 1 version, iOS >= 10, Android >= 5'
        // ],
        // additionalLegacyPolyfills: ['regenerator-runtime/runtime'], // 添加额外的 Polyfill
        // modernPolyfills: true, // 仅在现代浏览器中包括必要的 polyfill
        // renderLegacyChunks: true // 输出 legacy 的 bundle
      }),
      // gzip
      viteCompression({
        verbose: true, // 输出压缩结果的详细信息
        disable: false, // 是否禁用压缩
        threshold: 10240, // 只压缩大于 10kB 的文件
        algorithm: 'gzip', // 使用的压缩算法，'gzip' 是最常用的
        ext: '.gz', // 生成的压缩文件后缀
        deleteOriginFile: false // 是否删除源文件（通常不建议）
      }),
      // 静态资源 图片压缩
      // vite-plugin-imagemin 取决于项目是否有大量图片资源，如果有，可以考虑使用该插件压缩图片 已经有其他优化流程（比如TinyPNG 等）或者图片资源不多或者不变 则不需要安装也可
      imageminPlugin({
        // 配置 GIF 图片优化
        gifsicle: {
          optimizationLevel: 7, // 优化级别，范围是 1-7，数字越大优化越强
          interlaced: false // 是否启用交错，启用后图片会逐渐加载
        },
        // 配置 PNG 图片优化
        optipng: {
          optimizationLevel: 7 // 优化级别，范围是 0-7，数字越大优化越强
        },
        // 配置 JPEG 图片优化
        mozjpeg: {
          quality: 75 // 图片质量，范围是 0-100，数字越大质量越高
        },
        // 配置 PNG 图片优化，使用 pngquant 算法
        pngquant: {
          quality: [0.8, 0.9], // 图片质量范围，范围是 0-1，数字越大质量越高
          speed: 4 // 压缩速度，范围是 1-10，数字越大速度越快，但优化效果可能较差
        },
        // 配置 SVG 图片优化
        svgo: {
          plugins: [
            { name: 'removeViewBox' }, // 移除 SVG 的 viewBox 属性
            { name: 'removeEmptyAttrs', active: false } // 移除空属性，active 为 false 表示不启用此插件
          ]
        }
      }),
      createSvgIconsPlugin({
        // 指定需要缓存的图标文件夹
        iconDirs: [path.resolve(process.cwd(), 'public/icons')],
        // 指定symbolId格式
        symbolId: '[name]',
        customDomId: '__svg__icons__dom__'
      }),
      terser()
    ],
    build: {
      // target: 'es2015',// 将 targets 选项传递给 @vitejs/plugin-legacy 插件 二选一配置
      minify: 'terser', // 代码压缩
      assetsInlineLimit: 1024 * 10, // 静态资源大小限制 10kb 小于此阈值的导入或引用资源将内联为 base64 编码，以避免额外的 http 请求
      // 将 chunkSizeWarningLimit 设置为适合的值以便警告潜在的性能问题 chunk 大小警告的限制 500KB
      chunkSizeWarningLimit: 500,
      // 是否生成 sourcemap
      sourcemap: isProd ? false : true,
      rollupOptions: {
        // vite-plugin-cdn-import
        // 将某些第三方库设为外部依赖，减少包体积，提高性能。例如不常改变的库可以通过CDN引入 ['axios', 'lodash']
        // external: ['vue', 'vue-router'], // 打包时排除 vue 和 vue-router 依赖
        output: {
          // 静态资源文件名格式 默认
          // assetFileNames: 'assets/[name]-[hash][extname]',
          // 静态资源文件名格式 精准控制得用函数
          assetFileNames(assetInfo: PreRenderedAsset) {
            if (assetInfo.name === 'css') {
              return 'css/[name]-[hash].css';
            }
            if (assetInfo.name === 'js') {
              return 'js/[name]-[hash].js';
            }
            // 图片资源打包成图片文件
            if (isImageFile(assetInfo)) {
              return 'images/[name]-[hash].[ext]';
            }
            // 如果以上条件都不满足，返回默认的文件名格式
            return '[ext]/[name].[hash].[ext]';
          },
          // 输出文件名格式
          entryFileNames: 'js/[name]-[hash].js',
          chunkFileNames: 'js/[name]-[hash].js',
          // 代码分割配置
          manualChunks(id) {
            // 将 vue 及其相关包打包成一个文件 打包成 'vue.js'
            if (id.includes('vue')) {
              return 'vue';
            }
            // 将 axios 单独打包成一个文件 打包成 'axios.js'
            if (id.includes('node_modules/axios')) {
              return 'axios';
            }
            // 其他 node_modules 中的包按照名称进行分组
            if (id.includes('node_modules')) {
              const moduleName = id.split('node_modules/')[1].split('/')[0];
              return moduleName;
            }
          }
        }
      },
      // 移除方式一 生产的不需要 console、debugger
      terserOptions: {
        compress: {
          drop_console: true, // 移除 console.log
          drop_debugger: true // 移除 debugger
        }
      }
      // 移除方式二
      // esbuild: {
      //   drop: isProd ? ['console', 'debugger'] : []
      // }
    }
  };
});
